#pragma once
#include "camera.h"
#include <graphics.h>
#include "Animation.h"
#include <iostream>
#include "platform.h"
#include "player_id.h"
#include "bullet.h"
#include "Particle.h"

extern Atlas atlas_run_effect;
extern Atlas atlas_jump_effect;
extern Atlas atlas_land_effect;

extern IMAGE img_1P_cursor;
extern IMAGE img_2P_cursor;

extern std::vector<Bullet*> bullet_list;
extern std::vector<Platform> platform_list;

extern bool is_debug;

class player
{
public:
	player()
	{
		timer_attack_cd.set_wait_time(attack_cd);
		timer_attack_cd.set_one_shot(true);
		timer_attack_cd.set_callback([&]() { can_attack = true; });

		timer_invulnerable.set_wait_time(600);
		timer_invulnerable.set_one_shot(true);
		timer_invulnerable.set_callback([&](){is_invulnerable = false;});

		timer_invulnerable_blink.set_wait_time(75);
		timer_invulnerable_blink.set_callback([&]()
			{
				is_showing_sketch_frame = !is_showing_sketch_frame;
			});

		timer_run_effect_generation.set_wait_time(100);
		timer_run_effect_generation.set_callback([&]()
			{
				Vector2 particle_position;
				IMAGE* frame = atlas_run_effect.get_image(0);
				particle_position.x = position.x + (size.x - frame->getwidth()) / 2;
				particle_position.y = position.y + size.y - frame->getheight();
				particle_list.emplace_back(particle_position, &atlas_run_effect,45);
			});

		timer_die_effect_generation.set_wait_time(35);
		timer_die_effect_generation.set_callback([&]()
			{
				Vector2 particle_position;
				IMAGE* frame = atlas_run_effect.get_image(0);
				particle_position.x = position.x + (size.x - frame->getwidth()) / 2;
				particle_position.y = position.y + size.y - frame->getheight();
				particle_list.emplace_back(particle_position, &atlas_run_effect, 150);
			});

		animation_jump_effect.set_atlas(&atlas_jump_effect);
		animation_jump_effect.set_interval(40);
		animation_jump_effect.set_loop(false);
		animation_jump_effect.set_callback([&]()
			{
				is_jump_effect_visible = false;
			});

		animation_land_effect.set_atlas(&atlas_land_effect);
		animation_land_effect.set_interval(50);
		animation_land_effect.set_loop(false);
		animation_land_effect.set_callback([&]()
			{
				is_land_effect_visible = false;
			});

		timer_cursor_visibility.set_wait_time(2000);
		timer_cursor_visibility.set_one_shot(true);
		timer_cursor_visibility.set_callback([&]()
			{
				is_cursor_visible = false;
			});
	}
	~player() = default;

	void set_hp(int val)
	{
		hp = val;
	}

	int get_hp()
	{
		return hp;
	}
	int get_mp()
	{
		return mp;
	}

	virtual void on_attack() { }
	virtual void on_attack_ex() { }
	virtual void on_land()
	{
		is_land_effect_visible = true;
		animation_land_effect.reset();

		IMAGE* effect_frame = animation_land_effect.get_frame();
		position_land_effect.x = position.x + (size.x - effect_frame->getwidth()) / 2;
		position_land_effect.y = position.y + size.y - effect_frame->getheight();
	}
	virtual void on_run(float distance)
	{
		if (is_attack_ex)
			return;
		position.x += distance;
		timer_run_effect_generation.resume();
	}
	virtual void on_jump()
	{
		if (speed.y == 0)
			first_jump = true;
		if ((speed.y != 0 && jump_count <= 0) || is_attack_ex)
			return;
		jump_count--;
		if (speed.y != 0)
			speed.y = 0;
		speed.y += jump_speed;
		is_jump_effect_visible = true;
		animation_jump_effect.reset();

		IMAGE* effect_frame = animation_jump_effect.get_frame();
		position_jump_effect.x = position.x + (size.x - effect_frame->getwidth()) / 2;
		position_jump_effect.y = position.y + size.y - effect_frame->getheight();
	}
	virtual void on_update(int delta)
	{
		move_and_collide(delta);
		if (is_attack_ex)
			return;
		int direction = is_right_key_down - is_left_key_down;
		if (direction != 0)
		{
			is_facing_right = direction > 0 ? true : false;
			current_animation = is_facing_right ? &animation_run_right : &animation_run_left;
			float distance = direction * run_speed * delta;
			on_run(distance);
		}
		else
		{
			current_animation = is_facing_right ? &animation_idle_right : &animation_idle_left;
			timer_run_effect_generation.pause();
		}

		if (hp <= 0)
			current_animation = last_hurt_direction.x < 0 ? &animation_die_left : &animation_die_right;

		current_animation->on_update(delta);
		animation_jump_effect.on_update(delta);
		animation_land_effect.on_update(delta);

		timer_attack_cd.on_update(delta);
		timer_invulnerable.on_update(delta);
		timer_invulnerable_blink.on_update(delta);
		timer_run_effect_generation.on_update(delta);
		timer_cursor_visibility.on_update(delta);

		if (hp <= 0)
			timer_die_effect_generation.on_update(delta);

		particle_list.erase(std::remove_if(
			particle_list.begin(), particle_list.end(),
			[](const Particle& particle)
			{
				return !particle.check_valid();
			}),
			particle_list.end());
		for (Particle& particle : particle_list)
			particle.on_update(delta);

		if (is_showing_sketch_frame)
			sketch_image(current_animation->get_frame(), &img_sketch);
	}
	virtual void on_draw(const Camera& camera) 
	{
		if (is_jump_effect_visible)
			animation_jump_effect.on_draw(camera, (int)position_jump_effect.x, (int)position_jump_effect.y);

		if(is_land_effect_visible)
			animation_land_effect.on_draw(camera, (int)position_land_effect.x, (int)position_land_effect.y);

		for (const Particle& particle : particle_list)
			particle.on_draw(camera);

		if (hp > 0 && is_invulnerable && is_showing_sketch_frame)
			putimagePNG(camera, (int)position.x, (int)position.y, &img_sketch);
		else
			current_animation->on_draw(camera, (int)position.x, (int)position.y);

		if (is_cursor_visible)
		{
			switch (id)
			{
			case PlayerID::P1:
				putimagePNG(camera, (int)position.x + (size.x - img_1P_cursor.getwidth()) / 2,
					(int)position.y - img_1P_cursor.getheight(),&img_1P_cursor);
				break;
			case PlayerID::P2:
				putimagePNG(camera, (int)position.x + (size.x - img_2P_cursor.getwidth()) / 2,
					(int)position.y - img_1P_cursor.getheight(), &img_2P_cursor);
				break;
			}
		}

		if (is_debug)
		{
			setlinecolor(RGB(0, 125, 255));
			rectangle((int)position.x, (int)position.y, (int)(position.x + size.x), (int)(position.y + size.y));
		}
	}
	virtual void on_input(const ExMessage& msg)
	{
		switch (msg.message)
		{
		case WM_KEYDOWN:
			switch (id)
			{
			case PlayerID::P1:
				switch (msg.vkcode)
				{
				case 0x41:
					is_left_key_down = true;
					break;
				case 0x44:
					is_right_key_down = true;
					break;
				case 0x57:
					if (!jump_long)
					{
						jump_long = true;
						on_jump();
					}
					break;
				case 0x4A:
					if (can_attack)
					{
						on_attack();
						can_attack = false;
						timer_attack_cd.rerstart();
					}
					break;
				case 0x4B:
					if (mp >= 100)
					{
						on_attack_ex();
						mp = 0;
					}
					break;
				}
				break;
			case PlayerID::P2:
				switch (msg.vkcode)
				{
				case VK_LEFT:
					is_left_key_down = true;
					break;
				case VK_RIGHT:
					is_right_key_down = true;
					break;
				case VK_UP:
					if (!jump_long)
					{
						jump_long = true;
						on_jump();
					}
					break;
				case VK_NUMPAD1:
					if (can_attack)
					{
						on_attack();
						can_attack = false;
						timer_attack_cd.rerstart();
					}
					break;
				case VK_NUMPAD2:
					if (mp >= 100)
					{
						on_attack_ex();
						mp = 0;
					}
					break;
				}
				break;
			}
			break;
		case WM_KEYUP:
			switch (id)
			{
			case PlayerID::P1:
				switch (msg.vkcode)
				{
				case 0x41:
					is_left_key_down = false;
					break;
				case 0x44:
					is_right_key_down = false;
					break;
				case 0x57:
					jump_long = false;
					break;
				}
				break;
			case PlayerID::P2:
				switch (msg.vkcode)
				{
				case VK_LEFT:
					is_left_key_down = false;
					break;
				case VK_RIGHT:
					is_right_key_down = false;
					break;
				case VK_UP:
					jump_long = false;
					break;
				}
				break;
			}
			break;
		}
	}

	void make_invulnerable()
	{
		is_invulnerable = true;
		timer_invulnerable.rerstart();
	}

	void set_id(PlayerID id)
	{
		this->id = id;
	}
	const Vector2& get_size()
	{
		return size;
	}
	void set_position(float x, float y)
	{
		position = { x,y };
	}
	const Vector2& get_position()
	{
		return position;
	}
	void set_direction(PlayerID id)
	{
		if (id == PlayerID::P1)
		{
			current_animation = &animation_idle_right;
			is_facing_right = true;
			return;
		}
		if (id == PlayerID::P2)
		{
			current_animation = &animation_idle_left;
			is_facing_right = false;
			return;
		}
	}
	bool get_is_facing_right()
	{
		return is_facing_right;
	}
protected:
	int mp = 0;
	int hp = 100;

	Vector2 size;
	Vector2 position;
	Vector2 speed;

	Animation animation_idle_left;
	Animation animation_idle_right;
	Animation animation_run_left;
	Animation animation_run_right;
	Animation animation_attack_ex_left;
	Animation animation_attack_ex_right;
	Animation animation_jump_effect;
	Animation animation_land_effect;
	Animation animation_die_left;
	Animation animation_die_right;

	bool is_jump_effect_visible = false;
	bool is_land_effect_visible = false;

	Vector2 position_jump_effect;
	Vector2 position_land_effect;

	Animation* current_animation = nullptr;

	PlayerID id = PlayerID::P1;

	bool is_left_key_down = false;
	bool is_right_key_down = false;

	bool is_facing_right;

	int offset = 20;

	int attack_cd = 500;
	bool can_attack = true;
	Timer timer_attack_cd;

	bool is_attack_ex = false;

	float run_speed = 0.35f;
	const float gravity = 1.6e-3f;
	const float jump_speed = -0.70f;

	int jump_count = 2;
	bool jump_long = false;
	bool first_jump = false;
	bool jump_flag = true;

	IMAGE img_sketch;
	bool is_invulnerable = false;
	bool is_showing_sketch_frame = false;
	Timer timer_invulnerable;
	Timer timer_invulnerable_blink;

	std::vector<Particle> particle_list;

	Timer timer_run_effect_generation;
	Timer timer_die_effect_generation;

	bool is_cursor_visible = true;
	Timer timer_cursor_visibility;

	Vector2 last_hurt_direction;
protected:
	void move_and_collide(int delta)
	{
		float last_speed_y = speed.y;

		speed.y += gravity * delta;
		position += speed * (float)delta;

		if (hp <= 0)
			return;

		if (speed.y > 0)
		{
			if (is_attack_ex)
				jump_flag = false;
			if (!first_jump && jump_flag)
			{
				jump_count--;
				jump_flag = false;
			}
			for (int i = 0; i < platform_list.size(); i++)
			{
				const Platform::CollisionShape& shape = platform_list[i].shape;
				bool is_collide_x = (max(position.x + size.x, shape.right)) - min(position.x, shape.left)
					<= size.x + (shape.right - shape.left);
				bool is_collide_y = (shape.y >= position.y && shape.y <= position.y + size.y);
				if (is_collide_x && is_collide_y)
				{
					float delta_pos_y = speed.y * delta;
					float last_tick_foot_pos_y = position.y + size.y - delta_pos_y;
					if (last_tick_foot_pos_y <= shape.y)
					{
						position.y = shape.y - size.y;
						speed.y = 0;
						jump_count = 2;
						first_jump = false;
						jump_flag = true;

						if (last_speed_y != 0)
							on_land();
						break;
					}
				}
			}
		}
		if(!is_invulnerable)
		{
			for (int i = 0; i < bullet_list.size(); i++)
			{
				if (!bullet_list[i]->get_valid() || bullet_list[i]->get_collide_target() != id)
					continue;
				if (bullet_list[i]->check_collision(position, size))
				{
					make_invulnerable();
					bullet_list[i]->on_collide();
					bullet_list[i]->set_valid(false);
					hp -= bullet_list[i]->get_damage();
					mp += 10;
					last_hurt_direction = bullet_list[i]->get_position() - position;
					if (hp <= 0)
					{
						speed.x = last_hurt_direction.x < 0 ? 0.35f : -0.35f;
						speed.y = -1.0f;
					}
				}
			}
		}
	}
};